package net.csdn.business.mirror.service.impl.thirdapi;

import cn.hutool.json.JSONUtil;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.domain.mirror.MyGithubDto;
import net.csdn.business.common.domain.request.mirror.ListRepoDTO;
import net.csdn.business.common.domain.vo.mirror.MirrorNameSpaceVO;
import net.csdn.business.common.domain.vo.mirror.MirrorProjectPageVO;
import net.csdn.business.common.domain.vo.mirror.MirrorRepositoryVO;
import net.csdn.business.common.enums.ResultCodeEnum;
import net.csdn.business.common.exception.BusinessException;
import net.csdn.business.mirror.entity.bo.github.UserBO;
import net.csdn.business.mirror.service.ThirdApi;
import net.csdn.business.mirror.utils.HttpUtil;
import net.csdn.business.mirror.utils.StringUtil;
import net.csdn.business.mirror.utils.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Service
@Slf4j
@Primary
public class GithubApiImpl implements ThirdApi {

    // https://docs.github.com/zh/rest/users/users?apiVersion=2022-11-28
    @Value("${githubApi.userUrl:https://api.github.com/user}")
    private String userUrl;

    // https://docs.github.com/zh/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user
    @Value("${githubApi.userAllReposUrl:https://api.github.com/user/repos?sort=updated&direction=desc&per_page=100}")
    private String userAllReposUrl;

    // https://docs.github.com/en/rest/orgs/orgs?apiVersion=2022-11-28#list-organizations-for-the-authenticated-user
    @Value("${githubApi.userAllOrgsUrl:https://api.github.com/user/orgs?page=1&per_page=100}")
    private String userAllOrgsUrl;

    @Value("${githubApi.userAllOrgsErrorMsg:组织数据获取异常，请确认是否设置org权限}")
    private String userAllOrgsErrorMsg;

    // https://docs.github.com/zh/rest/repos/repos?apiVersion=2022-11-28#list-organization-repositories
    @Value("${githubApi.orgAllReposUrl:https://api.github.com/orgs/{org}/repos?sort=updated&direction=desc&type=all&page=1&per_page=100}")
    private String orgAllReposUrl;

    @Value("${githubApi.apiVersion:2022-11-28}")
    private String apiVersion;

    @Value("${githubApi.apiAccept:application/vnd.github+json}")
    private String apiAccept;


    // https://docs.github.com/zh/rest/repos/repos?apiVersion=2022-11-28#get-a-repository
    @Value("${githubApi.userRepoUrl:https://api.github.com/repos/}")
    private String userRepoUrl;

    @Override
    public UserBO getUser(String token) {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        String content = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, userUrl, buildHeader(token), null, null).getContent();
        UserBO user = JSONUtil.toBean(content, UserBO.class);
        if (Objects.isNull(user) || StringUtils.isEmpty(user.getLogin())) {
            // {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
            // {"message":"Not Found","documentation_url":"https://docs.github.com/rest/reference/users/#update-the-authenticated-user"}
            log.error("GithubApi getUser failed: {}", content);
            return null;
        }
        return user;
    }

    @Override
    public void listUserAllRepos(MyGithubDto myGithub,String namespace, Integer page, Integer pageSize) {
        if (StringUtils.isEmpty(myGithub.getToken())) {
            return;
        }
        if (Objects.isNull(page) || page < 1) {
            page = 1;
        }
        if (Objects.isNull(pageSize) || pageSize < 1 || pageSize > 100) {
            pageSize = 100;
        }
        Map<String, String> params = new HashMap<>();
        params.put("page", page.toString());
        params.put("per_page", pageSize.toString());
        String url;
        if (StringUtils.isEmpty(namespace)) {
            url = UrlUtil.getNewUrl(userAllReposUrl, params);
        } else {
            if (namespace.equals(myGithub.getThirdUsername())) {
                params.put("type", "owner");
                url = UrlUtil.getNewUrl(userAllReposUrl, params);
            } else {
                Map<String, Object> parameter = new HashMap<>();
                parameter.put("org", namespace);
                url = UrlUtil.getNewUrl(StringUtil.replace(orgAllReposUrl, parameter), params);
            }
        }

        HttpUtil.Response response = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, url, buildHeader(myGithub.getToken()), null, null, 10000);
        String content = response.getContent();
        int code=response.getCode();
        if(code!=200){
            switch (code){
                case 401:
                case 403:
                    log.error("GithubApi listUserAllRepos namespace: {}, token 无效: {}", namespace, content);
                    throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
                default:
                    log.error("GithubApi listUserAllRepos namespace: {}, failed reason: {}", namespace, content);
                    break;
            }
            return;
        }
        List<MyGithubDto.Repository> repos = JSONUtil.toList(content, MyGithubDto.Repository.class);
        myGithub.setItems(repos);
        String link = response.getHeaders().getOrDefault("Link", "");
        myGithub.setThirdTotalPage(1);
        if (!StringUtils.isEmpty(link)) {
            if (link.contains(">; rel=\"last\"")) {
                String[] urls = link.split(">; rel=\\\"last\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    myGithub.setThirdTotalPage(Integer.parseInt(lastParameters.get("page")));
                }
            } else if (link.contains(">; rel=\"prev\"")) {
                String[] urls = link.split(">; rel=\\\"prev\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    myGithub.setThirdTotalPage(Integer.parseInt(lastParameters.get("page")) + 1);
                }
            }
        }
    }

    @Override
    public List<MirrorNameSpaceVO> listUserNamespaces(String token, String thirdUsername) {
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
        }
        List<MirrorNameSpaceVO> result = new ArrayList<>();
        MirrorNameSpaceVO personal = new MirrorNameSpaceVO();
        personal.setPath(thirdUsername);
        personal.setName(thirdUsername);
        personal.setType("personal");
        result.add(personal);
        Map<String, String> params = new HashMap<>();
        params.put("page", "1");
        params.put("per_page", "100");
        String url = UrlUtil.getNewUrl(userAllOrgsUrl, params);
        HttpUtil.Response response = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, url, buildHeader(token), null, null, 10000);
        String content = response.getContent();
        int code=response.getCode();
        if(code!=200){
            log.error("GithubApi listUserNamespaces  failed reason: {}", content);
            throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
        }
        List<MirrorNameSpaceVO> orgNamespaces = JSONUtil.toList(content, MirrorNameSpaceVO.class);
        if(orgNamespaces!=null&&orgNamespaces.size()>0){
            for (MirrorNameSpaceVO namespace : orgNamespaces) {
                namespace.setType("organization");
                namespace.setName(namespace.getLogin());
                namespace.setPath(namespace.getLogin());
            }
        }
        result.addAll(orgNamespaces);
        return result;
    }

    public MyGithubDto.Repository userRepo(String token, String repoPath) {
        StringBuilder url=new StringBuilder(userRepoUrl);
        url.append(repoPath);
        HttpUtil.Response response = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, url.toString(), buildHeader(token), null, null, 10000);
        String content = response.getContent();
        int code=response.getCode();
        if(code!=200){
            log.error("GithubApi userRepo repoPath: {}, token 无效: {}", repoPath, content);
            throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
        }
        MyGithubDto.Repository repository = JSONUtil.toBean(content, MyGithubDto.Repository.class);
        return repository;
    }

    @Override
    public MirrorProjectPageVO getALlRepos(ListRepoDTO repoDTO) throws Exception{
        String token = repoDTO.getToken();
        String namespace=repoDTO.getNamespace();
        String thirdUserName=repoDTO.getThirdUserName();
        if (StringUtils.isBlank(token)) {
            throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
        }
        Integer page=repoDTO.getPage();
        Integer pageSize=repoDTO.getPageSize();
        if (page==null|| page < 1) {
            page = 1;
        }
        if (pageSize==null || pageSize < 1 || pageSize > 100) {
            pageSize = 100;
        }
        Map<String, String> params = new HashMap<>();
        params.put("page", page.toString());
        params.put("per_page", pageSize.toString());
        String url;
        if (StringUtils.isEmpty(namespace)) {
            url = UrlUtil.getNewUrl(userAllReposUrl, params);
        } else {
            if (namespace.equals(thirdUserName)) {
                params.put("type", "owner");
                url = UrlUtil.getNewUrl(userAllReposUrl, params);
            } else {
                Map<String, Object> parameter = new HashMap<>();
                parameter.put("org", namespace);
                url = UrlUtil.getNewUrl(StringUtil.replace(orgAllReposUrl, parameter), params);
            }
        }
        HttpUtil.Response response = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, url, buildHeader(token), null, null, 10000);
        String content = response.getContent();
        int code=response.getCode();
        if(code!=200){
            log.error("GithubApi listUserAllRepos namespace: {}, failed reason: {}", namespace, content);
            throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);

            /*switch (code){
                case 401:
                case 403:
                    log.error("GithubApi listUserAllRepos namespace: {}, token 无效: {}", namespace, content);
                    throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
                default:
                    log.error("GithubApi listUserAllRepos namespace: {}, failed reason: {}", namespace, content);
                    break;
            }
            return;*/
        }
        MirrorProjectPageVO result = new MirrorProjectPageVO();
        List<MirrorRepositoryVO> repos = JSONUtil.toList(content, MirrorRepositoryVO.class);
        result.setItems(repos);
        String link = response.getHeaders().getOrDefault("Link", "");
        if (!StringUtils.isEmpty(link)) {
            if (link.contains(">; rel=\"last\"")) {
                String[] urls = link.split(">; rel=\\\"last\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    result.setTotalPage(Integer.parseInt(lastParameters.get("page")));
                }
            } else if (link.contains(">; rel=\"prev\"")) {
                String[] urls = link.split(">; rel=\\\"prev\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    result.setTotalPage(Integer.parseInt(lastParameters.get("page")) + 1);
                }
            }
        }
        return result;
    }

    private Map<String, String> buildHeader(String token) {
        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", apiAccept);
        if(StringUtils.isNotBlank(token)){
            headers.put("Authorization", "Bearer " + token);
        }
        headers.put("X-GitHub-Api-Version", apiVersion);
        // GA 加速绑定Host
        headers.put("Host", "api.github.com");
        return headers;
    }
}